package com.xh.service.impl;

import com.xh.common.consts.CommonConst;
import com.xh.doamin.es.EsArticle;
import com.xh.doamin.vo.PageResult;
import com.xh.repository.EsArticleRepository;
import com.xh.service.IEsArticleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @Name EsArticleSerImpl
 * @Description
 * @Author wen
 * @Date 2019-05-24
 */
@Service
@Slf4j
public class EsArticleServiceImpl implements IEsArticleService {

    @Autowired
    private EsArticleRepository esArticleRepository;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public void deleteById(int id) {
        esArticleRepository.deleteById(id);
    }

    @Override
    public void delete(EsArticle esArticle) {
        esArticleRepository.delete(esArticle);
    }

    @Override
    public EsArticle save(EsArticle esArticle) {
        return esArticleRepository.save(esArticle);
    }

    @Override
    public EsArticle getById(int id) {
        return esArticleRepository.findById(id);
    }

    @Override
    public void batchInsert(List<EsArticle> esArticles) {
        esArticleRepository.saveAll(esArticles);
    }

    @Override
    public void deleteAll() {
        log.info("============ START删除es中所有文章缓存 ===========");
        esArticleRepository.deleteAll();
        log.info("============ END删除es中所有文章缓存成功 ===========");
    }

//    @Override
//    public PageResult<EsArticle> search(String keyword, int number) {
//        // 构建查询
//        NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();
//        // 多索引查询
//        searchQuery.withIndices("article").withTypes("article");
//        QueryBuilder boolQuery = QueryBuilders.boolQuery()
//                .must(QueryBuilders.multiMatchQuery(keyword, "title", "tags", "summary"));
////                .must(QueryBuilders.moreLikeThisQuery(new String[]{"title", "tags", "summary"}));
//        searchQuery.withQuery(boolQuery)
//            .withHighlightFields(new HighlightBuilder.Field("title"), new HighlightBuilder.Field("summary"))
//            .withPageable(PageRequest.of(number - 1, pageSize));
//        Page<EsArticle> page = esArticleRepository.search(searchQuery.build());
//        PageResult pageResult = new PageResult(page.getTotalElements(), page.getNumber() + 1, page.getTotalPages(), page.getContent());
//        System.err.println(pageResult);
//        return pageResult;
//    }

    @Override
    public PageResult<EsArticle> search(String keyword, int number, int size) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery(keyword, CommonConst.ARTICLE_TITLE_KEY, CommonConst.ARTICLE_SUMMARY_KEY).operator(Operator.AND))
                .withPageable(PageRequest.of((number - 1) * size, size))
                .withHighlightFields(new HighlightBuilder.Field(CommonConst.ARTICLE_TITLE_KEY), new HighlightBuilder.Field(CommonConst.ARTICLE_SUMMARY_KEY)) // 设置高亮
                .build();
        //分页高亮查询
        AggregatedPage<EsArticle> result = elasticsearchTemplate.queryForPage(searchQuery, EsArticle.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                if (response.getHits().totalHits == CommonConst.NUMBER_LONG_0) {
                    return new AggregatedPageImpl<>(Collections.emptyList(), pageable, CommonConst.NUMBER_LONG_0);
                }
                List<Object> list = new ArrayList<>();
                try {
                    for (SearchHit searchHit : response.getHits().getHits()) {
                        Object obj = ReflectUtils.newInstance(clazz);
                        //1.写入id字段值
                        FieldUtils.writeField(obj, CommonConst.ARTICLE_ID_KEY, Integer.valueOf(searchHit.getId()), true);
                        //2.非高亮字段的数据写入
                        for (Map.Entry<String, Object> entry : searchHit.getSourceAsMap().entrySet()) {
                            String key = entry.getKey();
                            Field field = FieldUtils.getField(clazz, key, true);
                            if (null == field) {
                                continue;
                            }
                            if (CommonConst.ARTICLE_CREATETIME_KEY.equals(key)) {
                                FieldUtils.writeField(obj, key, new Date((long) entry.getValue()), true);
                            } else {
                                FieldUtils.writeField(obj, key, entry.getValue(), true);
                            }
                        }
                        //3.高亮字段的数据写入
                        for (Map.Entry<String, HighlightField> entry : searchHit.getHighlightFields().entrySet()) {
                            StringBuilder sb = new StringBuilder();
                            Text[] fragments = entry.getValue().getFragments();
                            for (Text fragment : fragments) {
                                sb.append(fragment.toString());
                            }
                            //写入高亮的内容
                            FieldUtils.writeField(obj, entry.getKey(), sb.toString(), true);
                        }
                        list.add(obj);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                return new AggregatedPageImpl(list, pageable, response.getHits().totalHits);
            }
        });
        //不需要高亮就直接分页返回
//        Page<EsArticle> result = esArticleRepository.search(searchQuery);
        return new PageResult<>(result.getTotalElements(), result.getNumber() + 1, result.getTotalPages(), result.getContent());
    }


}
